/*
 * Function test of qtsm_lib_api using glib framework
 *
 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */
#include "gtest_qtsm_lib_comm.h"
#include <openssl/ecdsa.h>
#include <openssl/sha.h>
#include <openssl/objects.h>

static comm_req g_extend_pcr_reqs[] = {
    /* Index: boundary value */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 0,
                    .req_data_len = PCR_MAX_LEN_384,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = READ_ONLY_INDEX
            }
        }
    },
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 8,
                    .req_data_len = PCR_MAX_LEN_384,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = READ_ONLY_INDEX
            }
        }
    },
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 31,
                    .req_data_len = PCR_MAX_LEN_384,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_res_u = {
                .extend_pcr = {
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        }
    },
    /* Index: out of range */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 32,
                    .req_data_len = PCR_MAX_LEN_384,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = INVALID_INDEX
            }
        }
    },
    /* req_data_len: 0 or QTSM_PCR_MAX_LENGTH */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 16,
                    .req_data_len = QTSM_PCR_MAX_LENGTH,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = INVALID_ARGUMENT
            }
        }
    },
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 16,
                    .req_data_len = 0,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = INVALID_ARGUMENT
            }
        }
    },
    /* req_data_len: = QTSM_PCR_MAX_LENGTH +1 */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 16,
                    .req_data_len = QTSM_PCR_MAX_LENGTH + 1,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = INPUT_TOO_LARGE
            }
        }
    },
    /* req_data_len: = QTSM_PCR_MAX_LENGTH -1 */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 16,
                    .req_data_len = QTSM_PCR_MAX_LENGTH - 1,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = INVALID_ARGUMENT
            }
        }
    },
    /* req_data: NULL */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 16,
                    .req_data_len = PCR_MAX_LEN_384,
                    .req_data = NULL,
                    .res_data_len = PCR_MAX_LEN_384
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = INVALID_ARGUMENT
            }
        }
    },
    /* res_data_len: < PCR_MAX_LEN_384 */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 16,
                    .req_data_len = PCR_MAX_LEN_384,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = PCR_MAX_LEN_384 - 1
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = BUFFER_TOO_SMALL
            }
        }
    },
    /* res_data_len: 0 means res_data is null */
    {
        .reqmsg = {
            .type = EXTEND_PCR,
            .qtsm_message_req_u = {
                .extend_pcr = {
                    .index = 16,
                    .req_data_len = PCR_MAX_LEN_384,
                    .req_data = "0123456789abcdef0123456789abcdef"
                            "0123456789abcdef0123456789abcdef012",
                    .res_data_len = 0
                }
            }
        },
        .resmsg = {
            .type = ERROR_CODE,
            .qtsm_message_res_u = {
                .error_num = INVALID_ARGUMENT
            }
        }
    }
};

/* Currently qtsm-server only support sha384 */
static int openssl_extend_pcr(const void *pcr_old, const uint32_t pcr_old_len,
    const void *pcr_exdata, const uint32_t pcr_exdata_len,
    unsigned char *pcr_new)
{
    int rc;
    SHA512_CTX ctx;
    void *pcr_data = NULL;
    int pcr_data_len;

    if (!pcr_old || pcr_old_len == 0 || !pcr_exdata || pcr_exdata_len == 0)
        return -1;

    pcr_data_len = pcr_old_len + pcr_exdata_len;
    pcr_data = g_malloc0(pcr_data_len);

    memcpy(pcr_data, pcr_old, pcr_old_len);

    memcpy(pcr_data + pcr_old_len, pcr_exdata, pcr_exdata_len);

    rc = SHA384_Init(&ctx);
    if (rc != OPENSSL_OK) {
        g_print("SHA384_Init failed!!!\n");
        g_free(pcr_data);
        pcr_data = NULL;
        return -1;
    }

    rc = SHA384_Update(&ctx, pcr_data, pcr_data_len);
    if (rc != OPENSSL_OK) {
        g_print("SHA384_Update failed!!!\n");
        g_free(pcr_data);
        pcr_data = NULL;
        return -1;
    }

    rc = SHA384_Final(pcr_new, &ctx);
    if (rc != OPENSSL_OK) {
        g_print("SHA384_Final failed!!!\n");
        g_free(pcr_data);
        pcr_data = NULL;
        return -1;
    }

    g_free(pcr_data);
    pcr_data = NULL;

    return OPENSSL_OK;
}

void gtest_qtsm_extend_pcr(const void *qtsm_dev_fd)
{
    uint32_t index = 0;
    uint32_t pcr_data_len;
    uint8_t *pcr_data = NULL;
    uint32_t req_data_len;
    uint8_t *req_data = NULL;
    uint32_t pcr_len = PCR_MAX_LEN_384;
    uint8_t *pcr_old = NULL;
    uint8_t *pcr_new = NULL;
    bool locked;
    unsigned int i;
    int rc;

    for (i = 0; i < ARRAY_SIZE(g_extend_pcr_reqs); i++) {
        locked = false;
        /* malloc data to store pcr value */
        pcr_data_len = g_extend_pcr_reqs[i].reqmsg.qtsm_message_req_u.extend_pcr.res_data_len;
        if (pcr_data_len != 0)
            pcr_data = g_malloc0(pcr_data_len);

        index = g_extend_pcr_reqs[i].reqmsg.qtsm_message_req_u.extend_pcr.index;
        req_data = g_extend_pcr_reqs[i].reqmsg.qtsm_message_req_u.extend_pcr.req_data;
        req_data_len = g_extend_pcr_reqs[i].reqmsg.qtsm_message_req_u.extend_pcr.req_data_len;

        /* Used for openssl verification */
        pcr_old = g_malloc0(pcr_len);

        rc = qtsm_describe_pcr(*(int *)qtsm_dev_fd, index, &locked, pcr_old, &pcr_len);
        if (index >= QTSM_MAX_PCR_COUNT)
            g_assert_cmpint(rc, ==, INVALID_INDEX);
        else
            g_assert_cmpint(rc, ==, NO_ERROR);

        /* test extend pcr interface */
        rc = qtsm_extend_pcr(*(int *)qtsm_dev_fd, index, req_data, req_data_len, pcr_data, &pcr_data_len);
        if (rc != NO_ERROR) {
            /* check error code */
            if (locked == true)
                g_assert_cmpint(rc, ==, READ_ONLY_INDEX);
            else
                g_assert_cmpint(rc, ==,
                    g_extend_pcr_reqs[i].resmsg.qtsm_message_res_u.error_num);
        } else {
            /* check value */
            g_assert_cmpint(pcr_data_len, ==,
                g_extend_pcr_reqs[i].resmsg.qtsm_message_res_u.extend_pcr.res_data_len);

            /* openssl verify extend pcr */
            pcr_new = g_malloc0(pcr_len);

            rc = openssl_extend_pcr(pcr_old, pcr_len, req_data, req_data_len, pcr_new);
            g_assert_cmpint(rc, ==, OPENSSL_OK);
            g_assert_cmpmem(pcr_new, pcr_len, pcr_data, pcr_data_len);
        }

        g_free(pcr_data);
        pcr_data = NULL;

        g_free(pcr_old);
        pcr_old = NULL;

        g_free(pcr_new);
        pcr_new = NULL;
    }
}